using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using GameStateManagement.GravityWorld;
using GameStateManagement.Model.Entities;
using GameStateManagement.Model.Physics;
using GameStateManagement.View.Rendering;
using GameStateManagement.View.Screens;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace GameStateManagement.Model
{
    /// <summary>
    /// Contains rules and logic for how the game is played.
    /// </summary>
    public abstract class GameModel
    {
        #region Fields

        protected Texture2D Background;
        public GravityGameState GameState;
        protected GravityMap Map;
        #endregion

        #region Properties

        [XmlIgnore]
        public GameplayScreen HostingScreen { get; set; }

        public string GameMessage { get; set; }

        public string Name { get; set; }

        public CompositeEntity Entities { get; set; }

        public List<Renderable> Decorations { get; set; }

        public PhysicsModule Physics { get; set; }

        public List<string> TextureNameList { get; set; }

        public string BackgroundName { get; set; }

        #endregion

        #region Events

        #region Delegates

        public delegate void GameStateChangeEventHandler(object sender, EventArgs e);

        public delegate void NewMessageEventHandler(object sender, EventArgs e);

        public delegate void PlayerChangeEventHandler(object sender, EventArgs e);

        #endregion

        /// <summary>
        /// Event gets fired when game changes state.
        /// </summary>
        public event GameStateChangeEventHandler GameStateChanged;

        protected virtual void OnGameStateChanged(EventArgs e)
        {
            if (GameStateChanged != null)
                GameStateChanged(this, e);
        }


        /// <summary>
        /// Evemt gets fired when the gamemessage is updated.
        /// </summary>
        public event NewMessageEventHandler NewMessage;

        protected virtual void OnNewMessage(EventArgs e)
        {
            if (NewMessage != null)
                NewMessage(this, e);
        }

        /// <summary>
        /// Event gets fired when game changes player.
        /// </summary>
        public event PlayerChangeEventHandler PlayerChanged;

        protected virtual void OnPlayerChange(EventArgs e)
        {
            if (PlayerChanged != null)
                PlayerChanged(this, e);
        }

        protected virtual void SetGameMessage(String msg)
        {
            GameMessage = msg;
            OnNewMessage(EventArgs.Empty);
        }

        #endregion

        #region Public Methods

        #region Game LifeTime methods

        public void StartGame()
        {
            ChangeGameState(GravityGameState.PickupFairy);
        }

        public void PauseGame()
        {
            ChangeGameState(GravityGameState.Paused);
        }

        public void RestartGame()
        {
            ChangeGameState(GravityGameState.PickupFairy);
            InitializeModel(true);
        }

        public void QuitGame()
        {
            ChangeGameState(GravityGameState.Quiting);
        }

        public void ChangeMap(GravityMap map)
        {
            Map = map;
        }
        
        #endregion

        public IRenderable[] GetDecorations()
        {
            return Decorations.ToArray();
        }

        public virtual void Update(GameTime gameTime)
        {
            if (Physics != null)
            {
                Physics.ApplyForcesOnEntites(gameTime, Entities);
            }
            Entities.Update(gameTime);
        }

        public abstract void InitializeModel(bool forceReinisilaziton);

        protected abstract void InitializeTextureNameList();

        #endregion

        #region  Constructors

        protected GameModel()
        {
            Name = String.Empty;
        }

        protected GameModel(CompositeEntity entities, PhysicsModule physics, GameplayScreen gamePlayScreen)
        {
            Name = String.Empty;
            Entities = entities;
            Physics = physics;
            HostingScreen = gamePlayScreen;
        }

        #endregion

        protected void ChangeGameState(GravityGameState newState)
        {
            GameState = newState;
            OnGameStateChanged(EventArgs.Empty);
        }
    }
}